//
// Created by konglinglong on 2020/9/26.
//


#include <assert.h>
#include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <wait.h>
#include "../lib/doctest.h"
#include "../../lib/csp/include/cspApi.h"
#include "../../src/ccore/include/commDef.h"
#include "commDef.h"
#include "appSyncCtrl.h"

#define UT_APP_SYNC_TIMEOUT 20

typedef struct UtAppSyncCtrlInfo
{
    pid_t pid;
    sem_t *pParent2ChildSem;
    sem_t *pChild2ParentSem;
    char parent2ChildSemFile[256];
    char child2ParentSemFile[256];
    uint32_t syncCount;
} S_UtAppSyncCtrlInfo;

int32_t gUtAppTypeLocal;
S_UtAppSyncCtrlInfo gUtAppSyncCtrlInfo[APP_TYPE_BUTT];

void UT_AppSyncInit(void)
{
    int32_t rc;
    uint32_t loop;

    memset(gUtAppSyncCtrlInfo, 0, sizeof(gUtAppSyncCtrlInfo));

    for (loop = 0; loop < APP_TYPE_BUTT; loop++)
    {
        int semFd;

        snprintf(gUtAppSyncCtrlInfo[loop].parent2ChildSemFile, sizeof(gUtAppSyncCtrlInfo[loop].parent2ChildSemFile),
            "parent2Child_%s", gAppTypeStrTable[loop]);
        snprintf(gUtAppSyncCtrlInfo[loop].child2ParentSemFile, sizeof(gUtAppSyncCtrlInfo[loop].child2ParentSemFile),
            "child2Parent_%s", gAppTypeStrTable[loop]);

        semFd = open(gUtAppSyncCtrlInfo[loop].parent2ChildSemFile, O_CREAT | O_RDWR | O_TRUNC, 0666);
        assert(-1 != semFd);
        rc = ftruncate(semFd, 8192);
        assert(-1 != rc);
        gUtAppSyncCtrlInfo[loop].pParent2ChildSem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, semFd, 0);
        assert(MAP_FAILED != gUtAppSyncCtrlInfo[loop].pParent2ChildSem);
        rc = sem_init(gUtAppSyncCtrlInfo[loop].pParent2ChildSem, 1, 0);
        assert(-1 != rc);

        semFd = open(gUtAppSyncCtrlInfo[loop].child2ParentSemFile, O_CREAT | O_RDWR | O_TRUNC, 0666);
        assert(-1 != semFd);
        rc = ftruncate(semFd, 8192);
        assert(-1 != rc);
        gUtAppSyncCtrlInfo[loop].pChild2ParentSem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, semFd, 0);
        assert(MAP_FAILED != gUtAppSyncCtrlInfo[loop].pChild2ParentSem);
        rc = sem_init(gUtAppSyncCtrlInfo[loop].pChild2ParentSem, 1, 0);
        assert(-1 != rc);
    }

    for (loop = 0; loop < APP_TYPE_BUTT - 1; loop++)
    {
        gUtAppTypeLocal = loop + 1;
        gUtAppSyncCtrlInfo[loop].pid = 0;
        gUtAppSyncCtrlInfo[loop].pid = fork();
        assert(gUtAppSyncCtrlInfo[loop].pid >= 0);
        if (0 == gUtAppSyncCtrlInfo[loop].pid)
        {
            break;
        }
        else
        {
            gUtAppTypeLocal = 0;
        }
    }

    TEST_LOG("gUtAppTypeLocal[%d], pid[%d], app[%s]\n",
        gUtAppTypeLocal,
        getpid(),
        gAppTypeStrTable[gUtAppTypeLocal]);
    gUtAppSyncCtrlInfo[gUtAppTypeLocal].pid = getpid();
}

void UT_AppSyncFinal(void)
{
    uint32_t loop;
    int status;

    TEST_LOG("MemGetAllocSize = %u, MemAllocCount = %u, MemFreeCount = %u, app[%s]\n",
        OS_MemGetAllocSize(), OS_MemGetAllocCount(), OS_MemGetFreeCount(), gAppTypeStrTable[gUtAppTypeLocal]);
        REQUIRE(OS_MemGetAllocCount() == OS_MemGetFreeCount());

    if (0 == gUtAppTypeLocal)
    {
        for (loop = 0; loop < APP_TYPE_BUTT; loop++)
        {
            if (0 != loop)
            {
                waitpid(gUtAppSyncCtrlInfo[loop].pid, &status, 0);
            }
        }
        for (loop = 0; loop < APP_TYPE_BUTT; loop++)
        {
            munmap(gUtAppSyncCtrlInfo[loop].pParent2ChildSem, sizeof(sem_t));
            munmap(gUtAppSyncCtrlInfo[loop].pChild2ParentSem, sizeof(sem_t));
            unlink(gUtAppSyncCtrlInfo[loop].parent2ChildSemFile);
            unlink(gUtAppSyncCtrlInfo[loop].child2ParentSemFile);
        }
    }
    else
    {
        exit(0);
    }
}

void UT_AppSyncOnce(void)
{
    int ret;
    uint32_t loop;
    struct timespec ts;
    struct timeval tv;

    gUtAppSyncCtrlInfo[gUtAppTypeLocal].syncCount++;

    TEST_LOG("UT_AppSyncOnce() BEG. count[%8d], pid[%8d], app[%s]\n",
        gUtAppSyncCtrlInfo[gUtAppTypeLocal].syncCount, getpid(), gAppTypeStrTable[gUtAppTypeLocal]);

    if (0 == gUtAppTypeLocal)
    {
        for (loop = 0; loop < APP_TYPE_BUTT; loop++)
        {
            if (0 != loop)
            {
                gettimeofday(&tv, NULL);
                ts.tv_sec = tv.tv_sec + UT_APP_SYNC_TIMEOUT;
                ts.tv_nsec = tv.tv_usec;
                ret = sem_timedwait(gUtAppSyncCtrlInfo[loop].pChild2ParentSem, &ts);
                assert(0 == ret);
            }
        }

        for (loop = 0; loop < APP_TYPE_BUTT; loop++)
        {
            if (0 != loop)
            {
                ret = sem_post(gUtAppSyncCtrlInfo[loop].pParent2ChildSem);
                assert(0 == ret);
            }
        }
    }

    if (0 != gUtAppTypeLocal)
    {
        ret = sem_post(gUtAppSyncCtrlInfo[gUtAppTypeLocal].pChild2ParentSem);
        assert(0 == ret);

        gettimeofday(&tv, NULL);
        ts.tv_sec = tv.tv_sec + UT_APP_SYNC_TIMEOUT;
        ts.tv_nsec = tv.tv_usec;
        ret = sem_timedwait(gUtAppSyncCtrlInfo[gUtAppTypeLocal].pParent2ChildSem, &ts);
        assert(0 == ret);
    }

    TEST_LOG("UT_AppSyncOnce() END. count[%8d], pid[%8d], app[%s]\n",
        gUtAppSyncCtrlInfo[gUtAppTypeLocal].syncCount, getpid(), gAppTypeStrTable[gUtAppTypeLocal]);
}

int32_t UT_AppSyncIsXxApp(int32_t appType)
{
    return gUtAppTypeLocal == appType;
}
